package com.awesome.network.util;

import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.os.Build;
import android.telephony.TelephonyManager;

import java.util.ArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

/**
 * Author: JFangZ
 * Email:janezhang1901@163.com
 * Date: 2021/4/29 15:24
 * Description:Observer network state.
 */
public enum NetworkStateHelper {
    SINGLETON;
    private static final long GET_NETWORK_STATE_DELAY = 500;

    private Application mApplication = null;
    /**
     * 执行延时任务的线程池
     */
    private final ScheduledExecutorService mScheduledExecutorService = Executors.newSingleThreadScheduledExecutor(
            new ThreadFactory() {
                @Override
                public Thread newThread(Runnable runnable) {
                    Thread thread = new Thread(runnable);
                    thread.setName("getNetworkStateThread");
                    thread.setDaemon(true);
                    return thread;
                }
            });
    private final ArrayList<Callback> mListenerList = new ArrayList<>();
    private final NetworkCallback mNetworkCallback = new NetworkCallback() {
        @Override
        public void onAvailable(Network network) {
            super.onAvailable(network);
            // Because change network state need time,so delay for avoid get previous state.
            mScheduledExecutorService.schedule(new Runnable() {
                @Override
                public void run() {
                    doCallback(getNetworkState());
                }
            }, GET_NETWORK_STATE_DELAY, TimeUnit.MILLISECONDS);
        }

        @Override
        public void onLost(Network network) {
            super.onLost(network);
            // Because change network state need time,so delay for avoid get previous state.
            mScheduledExecutorService.schedule(new Runnable() {
                @Override
                public void run() {
                    doCallback(getNetworkState());
                }
            }, GET_NETWORK_STATE_DELAY, TimeUnit.MILLISECONDS);
        }
    };

    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // Because change network state need time,so delay for avoid get previous state.
            mScheduledExecutorService.schedule(new Runnable() {
                @Override
                public void run() {
                    doCallback(getNetworkState());
                }
            }, GET_NETWORK_STATE_DELAY, TimeUnit.MILLISECONDS);
        }
    };

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date:2021/4/29 17:46
     * Description:Get state by network type.
     */
    private State getNetworkClass(int subtype) {
        switch (subtype) {
            case TelephonyManager.NETWORK_TYPE_GPRS:
            case TelephonyManager.NETWORK_TYPE_GSM:
            case TelephonyManager.NETWORK_TYPE_EDGE:
            case TelephonyManager.NETWORK_TYPE_CDMA:
            case TelephonyManager.NETWORK_TYPE_1xRTT:
            case TelephonyManager.NETWORK_TYPE_IDEN:
                return State.GPRS_2G;
            case TelephonyManager.NETWORK_TYPE_UMTS:
            case TelephonyManager.NETWORK_TYPE_EVDO_0:
            case TelephonyManager.NETWORK_TYPE_EVDO_A:
            case TelephonyManager.NETWORK_TYPE_HSDPA:
            case TelephonyManager.NETWORK_TYPE_HSUPA:
            case TelephonyManager.NETWORK_TYPE_HSPA:
            case TelephonyManager.NETWORK_TYPE_EVDO_B:
            case TelephonyManager.NETWORK_TYPE_EHRPD:
            case TelephonyManager.NETWORK_TYPE_HSPAP:
            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
                return State.GPRS_3G;
            case TelephonyManager.NETWORK_TYPE_LTE:
            case TelephonyManager.NETWORK_TYPE_IWLAN:
            case 19:// TelephonyManager.NETWORK_TYPE_LTE_CA,该枚举被源码隐藏,所以写成固定值
                return State.GPRS_4G;
            case TelephonyManager.NETWORK_TYPE_NR:// 5G
                return State.GPRS_5G;
            default:
                return State.UNKNOWN;
        }
    }

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date:2021/4/29 17:46
     * Description:Notify listener.
     */
    private void doCallback(State state) {
        for (Callback callback : mListenerList) {
            callback.onChanged(state);
        }
    }

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date:2021/4/29 17:46
     * Description:Start monitor network state change.
     * Overload.
     */
    public void startObserver(Application application) {
        startObserver(application, Build.VERSION.SDK_INT);
    }

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date:2021/4/29 17:46
     * Description:Start monitor network state change.
     */
    public void startObserver(Application application, int sdkVersion) {
        this.mApplication = application;
        ConnectivityManager connectivityManager = (ConnectivityManager) application.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (sdkVersion >= Build.VERSION_CODES.N) {
            // API greater than or equal 24.
            connectivityManager.registerDefaultNetworkCallback(mNetworkCallback);
        } else if (sdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
            connectivityManager.registerNetworkCallback(new NetworkRequest
                    .Builder()
                    .build(), mNetworkCallback);
        } else {
            // API less than 21.
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
            application.registerReceiver(mBroadcastReceiver, intentFilter);
        }
    }

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date:2021/4/29 17:46
     * Description:Get network state.
     */
    public State getNetworkState() {
        ConnectivityManager connectivityManager = (ConnectivityManager) mApplication.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
        if (networkInfo == null) {
            return State.NONE;
        }
        int type = networkInfo.getType();
        if (type == ConnectivityManager.TYPE_WIFI) {
            return State.WIFI;
        }
        if (type == ConnectivityManager.TYPE_MOBILE) {
            int subtype = networkInfo.getSubtype();
            return getNetworkClass(subtype);
        }
        return State.UNKNOWN;
    }

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date:2021/4/29 17:46
     * Description:Get network is available.
     */
    public boolean isAvailable() {
        return getNetworkState() != State.NONE;
    }

    public void addCallback(Callback callback) {
        mListenerList.add(callback);
    }

    public void removeCallback(Callback callback) {
        mListenerList.remove(callback);
    }

    public enum State {
        UNKNOWN, NONE, GPRS_2G, GPRS_3G, GPRS_4G, GPRS_5G, WIFI
    }

    public interface Callback {
        void onChanged(State state);
    }
}